﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using static ttbit.form.FontData;

namespace TTttbit.form
{
    class TTPackageTool
    {
        class FileIndexInfo
        {
            public uint dataSeek; //4
            public uint dataLen; //8
            public uint nextIndexSeek; //12
            public string Name;//dyn
            public string Type; //dyn
            public bool IsGroup;//f
            public void Write(BinaryWriter writer)
            {
                writer.Write(dataSeek);
                writer.Write(dataLen);
                writer.Write((byte)(IsGroup ? 1 : 0));
                byte[] buf1 = System.Text.Encoding.ASCII.GetBytes(Name);
                writer.Write((byte)buf1.Length);
                writer.Write(buf1, 0, buf1.Length);
                byte[] buf2 = System.Text.Encoding.ASCII.GetBytes(Type);
                writer.Write((byte)buf2.Length);
                writer.Write(buf2, 0, buf2.Length);
            }
            public void Read(BinaryReader reader)
            {

                this.nextIndexSeek = reader.ReadUInt32();

                this.dataLen = reader.ReadUInt32();
                this.IsGroup = reader.ReadByte() > 0;

                byte namelen = reader.ReadByte();
                this.Name = System.Text.Encoding.ASCII.GetString(reader.ReadBytes(namelen));
                byte typelen = reader.ReadByte();
                this.Type = System.Text.Encoding.ASCII.GetString(reader.ReadBytes(typelen));
            }
        }

        const string magic = "TTPack_1";
        public PackGroup_Mem UnPack(BinaryReader stream)
        {
            PackGroup_Mem group = new PackGroup_Mem();
            uint beginseek = (uint)stream.BaseStream.Position;

            byte[] buf = stream.ReadBytes(8);
            var magicode = System.Text.Encoding.ASCII.GetString(buf);
            if (magicode != magic)
            {
                throw new Exception("Not a Pack File");
            }
            var paktypelen = stream.ReadByte();
            group.Name = "root";
            group.Type = System.Text.Encoding.ASCII.GetString(stream.ReadBytes(paktypelen));


            List<FileIndexInfo> allfiles = new List<FileIndexInfo>();
            FileIndexInfo info = null;
            uint seek = (uint)(stream.BaseStream.Position - beginseek);
            do
            {
                info = new FileIndexInfo();
                info.dataSeek = seek;// stream.ReadUInt32();
                info.Read(stream);
                seek = info.nextIndexSeek;



                allfiles.Add(info);

                stream.BaseStream.Position = beginseek + seek;

            } while (info.nextIndexSeek > 0);

            foreach (var f in allfiles)
            {
                stream.BaseStream.Position = beginseek + f.dataSeek;

                if (f.IsGroup)
                {
                    var item = UnPack(stream);
                    item.Name = f.Name;
                    //group 的Type 是自己保存的
                    stream.BaseStream.Position = f.dataSeek;
                    group.allres[f.Name] = item;
                }
                else
                {
                    PackGroup_Item item = new PackGroup_Item();
                    item.Name = f.Name;
                    item.Type = f.Type;
                    item.Bytes = stream.ReadBytes((int)f.dataLen);
                    group.allres[f.Name] = item;
                }
            }

            return group;
        }
        public void Pack(IPackGroup group, BinaryWriter stream)
        {
            uint beginseek = (uint)stream.BaseStream.Position;

            var bts = System.Text.Encoding.ASCII.GetBytes(magic);
            stream.Write(bts, 0, 8);
            var bts2 = Encoding.ASCII.GetBytes(group.Type);
            if (bts2.Length > 255)
                throw new Exception("error type file.");
            stream.Write((byte)bts2.Length);
            stream.Write(bts2, 0, bts2.Length);

            var files = group.GetAllResourceName();
            List<FileIndexInfo> fileinfos = new List<FileIndexInfo>();
            List<IPackItem> items = new List<IPackItem>();

            foreach (var file in files)
            {
                var item = group.GetPackItem(file);
                FileIndexInfo info = new FileIndexInfo();
                info.dataSeek = (uint)(stream.BaseStream.Position - beginseek); //先暂时占个位置
                info.nextIndexSeek = 0;
                if (!info.IsGroup)
                {
                    info.dataLen = (uint)item.Bytes.Length;
                    info.Type = item.Type;
                }
                else
                {
                    info.dataLen = 0;
                    info.Type = "";
                }

                info.Name = item.Name;
                info.Write(stream);
                fileinfos.Add(info);
                items.Add(item);
            }
            uint dataseek = (uint)(stream.BaseStream.Position - beginseek);

            for (var i = 0; i < fileinfos.Count; i++)
            {
                var info = fileinfos[i];
                info.dataSeek = dataseek;
                if (i == fileinfos.Count - 1)
                {
                    info.nextIndexSeek = 0;
                }
                else
                {
                    info.nextIndexSeek = fileinfos[i + 1].dataSeek;//之前暂存作为indexpos
                }
                if (info.IsGroup)
                {
                    using var ms = new MemoryStream();
                    var binWriter = new System.IO.BinaryWriter(ms);
                    Pack(items[i] as IPackGroup, binWriter);
                    var bytes = ms.ToArray();
                    stream.Write(bytes);
                }
                else
                {
                    stream.Write(items[i].Bytes);
                }
                dataseek = (uint)(stream.BaseStream.Position - beginseek);
            }

            //回头更新文件信息
            for (var i = 0; i < fileinfos.Count; i++)
            {
                stream.BaseStream.Position = beginseek + fileinfos[i].dataSeek;
                fileinfos[i].Write(stream);
            }
        }
    }
}
